/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package analisador.lexico;

import java.util.ArrayList;

/**
 *
 * @author Danilo
 */
public class GeracaoCodigo {
    
    Token token;
    String frase = "";
    String codigo = "#include <stdio.h>\n#include <stdlib.h>\n\n";
    String tipo = "";
    String variaveis = "";
    boolean erro=false;
    private String nomeFuncao = "";
    String parametros = "";
    private String parametroAux = "";
    private boolean lendo;
    private boolean passVerifica2;
    public GeracaoCodigo(Token valor){
        token = valor;
    }

    public String getCodigo() {
        return codigo;
    }


    //Regra 1
    public void programa() throws ExcecaoCompilador{
        token.getToken();
        declaracoes();
        if(token.equals("algoritmo")){
            codigo += "int main() {\n";
            token.getToken();
            corpo();
        }
        
        codigo +="return 0;\n}";
        frase += "Fim da compilacao" + "\n";
        System.out.println(codigo);
        
    }

    //Regra 2
    public void declaracoes() throws ExcecaoCompilador{
        if(token.equals("declare") ||token.equals("constante") || token.equals("tipo") || token.equals("procedimento") || token.equals("funcao")){
            decl_local_global();
            if(tipo.equals("char")){
                codigo += "char "+variaveis+ "[80];\n";    
            }
            codigo += tipo + " "+ variaveis + ";\n";
            tipo = "";
            variaveis = "";
            declaracoes();
        }
    }

    //Regra 3
    public void decl_local_global() throws ExcecaoCompilador{
        
        if(token.equals("declare") ||token.equals("constante") || token.equals("tipo")){
            if(token.equals("tipo")){
                codigo += "typedef ";
            }
            declaracao_local();
        }
        else
            declaracao_global();        
    }

    //Regra 4
    public void declaracao_local() throws ExcecaoCompilador{
        if(token.equals("declare")){
            token.getToken();
            variavel();
            codigo += tipo + " "+ variaveis + ";\n";
        }else
            if(token.equals("constante")){
                //ident
                
                token.getToken();
                if(token.equals("identificador")){
                        token.getToken();
                    if(token.equals(":")){

                        token.getToken();
                        tipo_basico();
                        if(token.equals("=")){

                            token.getToken();
                            valor_constante();
                        
                    }
            }else
                if(token.equals("tipo")){
                    //ident
                    token.getToken();
                    if(token.equals("identificador")){
                        token.getToken();
                        if(token.equals(":")){
                            token.getToken();
                            tipo();
                        
                        }
                }
    }}}}

    //Regra 5
    public void variavel() throws ExcecaoCompilador{
        identificador();
        mais_ident();
        if(token.equals(":")){
            token.getToken();
            tipo();
        }
            
    }

    //Regra 6
    public void identificador() throws ExcecaoCompilador{     
        ponteiro_opcional();
            if(token.equals("identificador")){
                variaveis += token.getNome();
                token.getToken();
                outros_ident();
                dimensao();
                
         
                }
            
    }

    //Regra 7
    public void ponteiro_opcional() throws ExcecaoCompilador{
        if(token.equals("^")){
            codigo += "*";
            token.getToken();//Consome token OK
    }}

    //Regra 8
    public void outros_ident() throws ExcecaoCompilador{
        if(token.equals(".")){
            codigo += ".";
            token.getToken();
            if(token.equals("identificador")){
                token.getToken();
                outros_ident();
            
                }
        }
    }

    //Regra 9
    public void dimensao() throws ExcecaoCompilador{
        if(token.equals("[")){
            token.getToken();
            variaveis+="[";
            exp_aritmetica();
            if(token.equals("]")){
                variaveis+="]";
                token.getToken();
                dimensao();
           
                }
        }
    }

    //Regra 10
    public void tipo() throws ExcecaoCompilador{
        if(token.equals("registro"))
            registro();   
        else
            tipo_estendido();          
            
    }

    //Regra 11
    public void mais_ident() throws ExcecaoCompilador{
        if(token.equals(",")){
            variaveis += ",";
            token.getToken();
            identificador();
            mais_ident();
        }
    }

    //Regra 12
    public void mais_variaveis() throws ExcecaoCompilador{
        if(token.equals("^") || token.equals("identificador")){
            variavel();
            mais_variaveis();
        }
    }

    //Regra 13
    public void tipo_basico() throws ExcecaoCompilador{
        if(token.equals("literal") ||token.equals("inteiro") || token.equals("real") || token.equals("logico")){
            if(token.equals("literal"))
                tipo = "char ";
            if(token.equals("inteiro") || token.equals("logico"))
            {
                tipo = "int ";}
            if(token.equals("real"))
                tipo = "float ";
  
            token.getToken();
            
        }
    }

    //Regra 14
    public void tipo_basico_ident() throws ExcecaoCompilador{
        if(token.equals("literal") ||token.equals("inteiro") || token.equals("real") || token.equals("logico")){
            tipo_basico();
        }else
            if(token.equals("identificador"))
                token.getToken();
            
    }

    //Regra 15
    public void tipo_estendido() throws ExcecaoCompilador{
        ponteiro_opcional();
        tipo_basico_ident();
    }

    //Regra 16
    public void valor_constante() throws ExcecaoCompilador{
        if(token.equals("cadeia_literal")||token.equals("numero_inteiro")||token.equals("numero_real") ||token.equals("verdadeiro")||token.equals("falso"))
            token.getToken();
       
    }
    //Regra 17
    public void registro() throws ExcecaoCompilador{
        if(token.equals("registro")){
            codigo += "struct{";
            token.getToken();
            variavel();
            mais_variaveis();
            codigo += tipo + " "+ variaveis + ";\n";
            if(token.equals("fim_registro")){
                codigo += "}\n";
                token.getToken();
            }}
            tipo = "";
            variaveis = "";
    }

    //Regra 18
    public void declaracao_global() throws ExcecaoCompilador{
        if(token.equals("procedimento")){
            token.getToken();
            codigo += "void "+ token.getNome();
            if(token.equals("identificador")){
                token.getToken();
                if(token.equals("(")){
                    codigo += "(";
                    token.getToken();
                    parametros_opcional();
                    if(token.equals(")")){
                        codigo += "){\n";
                        token.getToken();
                        declaracoes_locais();
                        comandos();
                        if(token.equals("fim_procedimento")){
                            codigo += "}\n";
                            token.getToken();
                            
                        }
                    }}}              
        }else{
            if(token.equals("funcao")){
                token.getToken();
                if(token.equals("identificador")){
                    nomeFuncao = token.getNome();
                    token.getToken();
                    if(token.equals("(")){
                        token.getToken();
                        parametros_opcional();
                        if(token.equals(")")){
                            token.getToken();
                            if(token.equals(":")){
                                token.getToken();
                                tipo_estendido();
                                codigo+= tipo + " "+ nomeFuncao + "("+ parametros+"){\n";
                                declaracoes_locais();
                                comandos();
                                if(token.equals("fim_funcao"))
                                    codigo +="}\n";
                                    token.getToken();
                            }}}}}
        }
    }

    //Regra 19
    public void parametros_opcional() throws ExcecaoCompilador{
        if (token.equals("var") || token.equals("identificador") || token.equals("^"))
           parametro();
    }

    //Regra 20
    public void parametro() throws ExcecaoCompilador{
           var_opcional();
           identificador();
           mais_ident();
           if(token.equals(":")){
               token.getToken();
               tipo_estendido();
               parametros += tipo +" "+ variaveis ;
               mais_parametros();
           }
    }

    //Regra 21
    public void var_opcional() throws ExcecaoCompilador{
        if(token.equals("var"))
            token.getToken();
    }

    //Regra 22
    public void mais_parametros() throws ExcecaoCompilador{
        if(token.equals(",")){
            token.getToken();
            parametro();
        }
    }

    //Regra 23
    public void declaracoes_locais() throws ExcecaoCompilador{
        if(token.equals("declare") ||token.equals("constante") ||token.equals("tipo")){
            declaracao_local();
            declaracoes_locais();
        }
    }

    //Regra 24
    public void corpo() throws ExcecaoCompilador{
            declaracoes_locais();
            comandos();         
    }

    //Regra 25
    public void comandos() throws ExcecaoCompilador{
        if(token.equals("leia") ||token.equals("escreva") || token.equals("se") 
                ||token.equals("caso") ||token.equals("para") ||token.equals("enquanto") 
                ||token.equals("faca") ||token.equals("^") ||token.equals("identificador") ||token.equals("retorne")){
            cmd();
            comandos();
        }
    }

    //Regra 26 falta switch
    public void cmd() throws ExcecaoCompilador{
        if(token.equals("leia")){
            token.getToken();
            codigo += "scanf";
            if(token.equals("(")){
                token.getToken();
                codigo += "(";
                lendo = true;
                parametroAux = "inteiro";
                verificaTipo(parametroAux);
                identificador();
                mais_ident();
                if(token.equals(")")){
                    token.getToken();
                    codigo += ");\n";
                    lendo = false;
                }}
        }else
            if(token.equals("escreva")){
                token.getToken();
                codigo += "printf";
                if(token.equals("(")){
                    token.getToken();
                    codigo += "(";
                    expressao();
                    mais_expressao();
                    if(token.equals(")")){
                        token.getToken();
                        if(!passVerifica2)
                            codigo += "\"";
                        codigo += ");\n";
                    }}
            }else
                if(token.equals("se")){
                    token.getToken();
                    codigo += "if( ";
                    expressao();
                    if(token.equals("entao")){
                        token.getToken();
                        codigo += "){\n";
                        comandos();
                        codigo += "\n}\n";
                        senao_opcional();
                        if(token.equals("fim_se")){
                            token.getToken();
                            
                        }}
                }else
                    if(token.equals("caso")){
                        token.getToken();
                        exp_aritmetica();
                        if(token.equals("seja")){
                            token.getToken();
                            selecao();
                            senao_opcional();
                            if(token.equals("fim_caso")){
                                token.getToken();
                            }}
                    }else
                        if(token.equals("para")){
                            token.getToken();
                            codigo += "for(";
                            if(token.equals("identificador")){
                                parametroAux = token.getNome();
                                codigo += token.getNome();
                                token.getToken();                              
                                if(token.equals("<-")){
                                    codigo += "=";
                                    token.getToken();
                                    exp_aritmetica();
                                    if(token.equals("ate")){
                                        codigo += "; "+parametroAux;
                                        token.getToken();
                                        codigo += " <= ";
                                        exp_aritmetica();
                                        codigo += "; "+parametroAux+"++";
                                        if(token.equals("faca")){
                                            token.getToken();
                                            codigo += "){\n";
                                            comandos();
                                            if(token.equals("fim_para")){
                                                token.getToken();
                                                codigo += "\n};\n";
                                            }}}}}
                        }else
                            if(token.equals("enquanto")){
                                token.getToken();
                                codigo += "\nwhile(";
                                expressao();
                                if(token.equals("faca")){
                                    token.getToken();
                                    codigo += "){\n";
                                    comandos();
                                    if(token.equals("fim_enquanto")){
                                        token.getToken();
                                        codigo += "\n}\n";
                                    }}
                            }else
                                if(token.equals("faca")){
                                    token.getToken();
                                    codigo += "\ndo {\n";
                                    comandos();
                                    if(token.equals("ate")){
                                        token.getToken();
                                        codigo += "} while (";
                                        expressao();
                                        codigo += ");\n";
                                    }
                                }else
                                    if(token.equals("^")){
                                        token.getToken();
                                        codigo += "*";
                                        if(token.equals("identificador")){
                                            token.getToken();
                                            outros_ident();
                                            dimensao();
                                            if(token.equals("<-")){
                                                token.getToken();
                                                expressao();
                                            }}
                                    }else
                                        if(token.equals("identificador")){
                                            codigo += token.getNome();
                                            token.getToken();
                                            chamada_atribuicao();
                                        }else
                                            if(token.equals("retorne")){
                                                token.getToken();
                                                codigo += "return ";
                                                expressao();
                                            }
    }

    //Regra 27
    public void mais_expressao() throws ExcecaoCompilador{
        if(token.equals(",")){
            token.getToken();
            expressao();
            mais_expressao();
        }
    }

    //Regra 28
    public void senao_opcional() throws ExcecaoCompilador{
        if(token.equals("senao")){
            token.getToken();
            codigo += "else {\n";
            comandos();
            codigo += "}\n";
        }
    }

    //Regra 29
    public void chamada_atribuicao() throws ExcecaoCompilador{
        if(token.equals("(")){
            token.getToken();
            codigo += "(";
            expressao();
            mais_expressao();
            if(token.equals(")")){
                token.getToken();
                codigo += ")";
            }
        }else{
            //token.getToken();
            outros_ident();
            dimensao();
            if(token.equals("<-")){
                codigo += "=";
                token.getToken();
                expressao();
                codigo += ";";
            }
        }
    }

    //Regra 30
    public void selecao() throws ExcecaoCompilador{ 
            constantes();
            if(token.equals(":")){
                token.getToken();
                comandos();
                mais_selecao();
            }
    }

    //Regra 31
    public void mais_selecao() throws ExcecaoCompilador{
        if(token.equals("-") ||token.equals("numero_inteiro")){
            selecao();
        }
       
    }

    //Regra 32
    public void constantes() throws ExcecaoCompilador{
            numero_intervalo();
            mais_constantes();
    }

    //Regra 33
    public void mais_constantes() throws ExcecaoCompilador{
        if(token.equals(",")){
            token.getToken();
            constantes();
        }
    }

    //Regra 34
    public void numero_intervalo() throws ExcecaoCompilador{
            op_unario();
            if(token.equals("numero_inteiro")){
                token.getToken();
                intervalo_opcional();
            }
    }

    //Regra 35
    public void intervalo_opcional() throws ExcecaoCompilador{
        if(token.equals("..")){
            token.getToken();
            op_unario();
            if(token.equals("numero_inteiro")){
                token.getToken();
            }
        }
    }

    //Regra 36
    public void op_unario() throws ExcecaoCompilador{
        if(token.equals("-")){
            token.getToken();//OK
            codigo += " - ";
        }
    }

    //Regra 37
    public void exp_aritmetica() throws ExcecaoCompilador{
            termo();
            outros_termos();

    }

    //Regra 38
    public void op_multiplicacao() throws ExcecaoCompilador{
        if(token.equals("*")){
            token.getToken();
            codigo += "*";
        }else
            if(token.equals("/")){
                token.getToken();
                codigo += "/";
            }
    }

    //Regra 39
    public void op_adicao() throws ExcecaoCompilador{
        if(token.equals("+")){
            token.getToken();
            codigo += "+";
        }else
            if(token.equals("-")){
                token.getToken();
                codigo += "-";
            }
    }

    //Regra 40
    public void termo() throws ExcecaoCompilador{
            fator();
            outros_fatores();       
    }

    //Regra 41
    public void outros_termos() throws ExcecaoCompilador{
        if(token.equals("+") ||token.equals("-")){
            op_adicao();
            termo();
            outros_termos();
        }
    }

    //Regra 42
    public void fator() throws ExcecaoCompilador{
            parcela();
            outras_parcelas();
    }

    //Regra 43
    public void outros_fatores() throws ExcecaoCompilador{
        if(token.equals("*") ||token.equals("/")){
            
            op_multiplicacao();
            fator();
            outros_fatores();
        }
    }

    //Regra 44
    public void parcela() throws ExcecaoCompilador{
        if(token.equals("-") ||token.equals("^") ||token.equals("identificador") ||token.equals("numero_inteiro") ||token.equals("numero_real") ||token.equals("(")){
            op_unario();
            
            parcela_unario();
        }else
            //if(token.equals("&")){
            
            parcela_nao_unario();
            //}else
              //  frase = "Linha "+ token.getLinha() +": erro sintatico proximo a " + token.getNome() + "\n";//ERRO
    }

    //Regra 45
    public void parcela_unario() throws ExcecaoCompilador{
        if(token.equals("^")){
            codigo += "*";
            token.getToken();
            if(token.equals("identificador")){
                codigo += token.getNome();
               token.getToken();
               outros_ident();
               dimensao();
            }
            
        }else
            if(token.equals("identificador")){
                parametroAux = "inteiro";
                verificaTipo2(parametroAux);
                token.getToken();
                chamada_partes();
            }else
                if(token.equals("numero_inteiro") || token.equals("numero_real")){
                    codigo += token.getNome();
                    token.getToken();
                }else
                    if(token.equals("(")){
                        token.getToken();
                        expressao();
                        if(token.equals(")"))
                            token.getToken();
                        }
    }

    //Regra 46
    public void parcela_nao_unario() throws ExcecaoCompilador{
        if(token.equals("&")){
            codigo += "&&";
            token.getToken();
            if(token.equals("identificador")){
                codigo += token.getNome();
                token.getToken();
                outros_ident();
                dimensao();
            }
        }else
            if(token.equals("cadeia_literal")){
                codigo += token.getNome().substring(0, token.getNome().length()-1)+" ";
                token.getToken();

                }
 }

    //Regra 47
    public void outras_parcelas() throws ExcecaoCompilador{
        if(token.equals("%")){
            token.getToken();
            codigo += "%";
            parcela();
            outras_parcelas();
        }
    }

    //Regra 48
    public void chamada_partes() throws ExcecaoCompilador{
        if(token.equals("(")){
            token.getToken();
            expressao();
            mais_expressao();
            if(token.equals(")"))
                token.getToken();
            
        }else
            if(token.equals(".") || token.equals("[")){
                outros_ident();
                dimensao();
            }
    }

    //Regra 49
    public void exp_relacional() throws ExcecaoCompilador{
            exp_aritmetica();
            op_opcional();
       
    }

    //Regra 50
    public void op_opcional() throws ExcecaoCompilador{
        if(token.equals("=") ||token.equals("<>") ||token.equals(">=") ||token.equals("<=") ||token.equals(">") ||token.equals("<")){
            op_relacional();
            exp_aritmetica();
        }
    }

    //Regra 51
    public void op_relacional() throws ExcecaoCompilador{
        if(token.equals("=")){
           token.getToken();
           codigo += "=";
        }else
            if(token.equals("<>")){
                token.getToken();
                codigo += "<>";
            }else
                if(token.equals(">=")){
                    token.getToken();
                    codigo += ">=";
                }else
                    if(token.equals("<=")){
                        token.getToken();
                        
                        codigo += "<=";
                    }else
                        if(token.equals(">")){
                            token.getToken();
                            codigo += ">";
                        }else
                            if(token.equals("<")){
                                token.getToken();
                                codigo += "<";
                            }
    }

    //Regra 52
    public void expressao() throws ExcecaoCompilador{
            termo_logico();
            outros_termos_logicos();
    }

    //Regra 53
    public void op_nao() throws ExcecaoCompilador{
        if(token.equals("nao")){
            token.getToken();
            codigo += "!";
        }
    }

    //Regra 54
    public void termo_logico() throws ExcecaoCompilador{
            fator_logico();
            outros_fatores_logicos();
    }

    //Regra 55
    public void outros_termos_logicos() throws ExcecaoCompilador{
        if(token.equals("ou")){
            token.getToken();
            codigo += "||";
            termo_logico();
            outros_termos_logicos();
        }
    }

    //Regra 56
    public void outros_fatores_logicos() throws ExcecaoCompilador{
        if(token.equals("e")){
            token.getToken();
            codigo += "&&";
            fator_logico();
            outros_fatores_logicos();
        }
    }

    //Regra 57
    public void fator_logico() throws ExcecaoCompilador{
            op_nao();
            parcela_logica();
      
    }

    //Regra 58
    public void parcela_logica() throws ExcecaoCompilador{
        if(token.equals("verdadeiro") || token.equals("falso"))
            token.getToken();
        else
            exp_relacional();
    }

    private void verificaTipo(String parametroAux) {
        
        if(parametroAux.equals("inteiro")||parametroAux.equals("logico")){
            codigo += "\"%d\", &"+ token.getNome();
        }else
            if(parametroAux.equals("real")){
                codigo += "\"%f\"";
            }else
                if(parametroAux.equals("literal")){
                    codigo += "\"%c\", &" + token.getNome();
                }
    }

    private void verificaTipo2(String parametroAux) {
      passVerifica2 = true;  
      if(parametroAux.equals("inteiro")||parametroAux.equals("logico")){
            codigo += "\"%d\", "+ token.getNome();
        }else
            if(parametroAux.equals("real")){
                codigo += "%f\"," + token.getNome();
            }else
                if(parametroAux.equals("literal")){
                    codigo += "%c\", " + token.getNome();
                }
    }
    
}
